<?php
/**
 * This file is part of the n98-magerun2 project.
 *
 * For the full copyright and license information, please view the MIT-LICENSE.txt
 * file that was distributed with this source code.
 */

namespace N98\Magento\Command\Database;

use N98\Magento\Command\TestCase;
use N98\Util\OperatingSystem;
use PHPUnit\Framework\ExpectationFailedException;

/**
 * @see \N98\Magento\Command\Database\DumpCommand
 */
class DumpCommandTest extends TestCase
{
    public function testExecute()
    {
        $input = [
            'command'        => 'db:dump',
            '--add-time'     => true,
            '--only-command' => true,
            '--force'        => true,
            '--compression'  => 'gz',
        ];

        // Accept both mysqldump and mariadb-dump for compatibility
        try {
            $this->assertDisplayContains($input, 'mysqldump');
        } catch (ExpectationFailedException $e) {
            $this->assertDisplayContains($input, 'mariadb-dump');
        }
        $this->assertDisplayContains($input, '.sql');
        $this->assertDisplayContains($input, '.sql.gz');
    }

    /**
     * @see filenamePatterns
     */
    public function provideFilenamePatternsAndOptions()
    {
        return [
            # testAddTimeAutogenerated
            ['/^.*[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}\.sql$/', []],
            # testAddTimePrefixAutogenerated
            ['/^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}.*\.sql$/', ['--add-time' => 'prefix']],
            # testAddTimeFilenameSpecified
            ['/^.*[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}\.sql.gz$/', ['--compression' => 'gzip']],
            # testAddTimeFilenameSpecified
            ['/^foo_[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}\.sql$/', ['filename' => 'foo.sql']],
            # testAddTimePrefixFilenameSpecified
            ['/^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}_foo\.sql$/', ['filename' => 'foo.sql', '--add-time' => 'prefix']],
            # testAddTimeOffFilenameSpecified
            ['/^foo.sql$/', ['filename' => 'foo.sql', '--add-time' => 'no']],
            # testAddTimeFilenameSpecifiedRelative
            ['/^..\/foo_[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}\.sql$/', ['filename' => '../foo.sql']],
        ];
    }

    /**
     * @test
     * @dataProvider provideFilenamePatternsAndOptions
     *
     * @param string $regex
     * @param array $options
     * @return void
     */
    public function filenamePatterns($regex, array $options)
    {
        $mandatory = [
            'command'               => 'db:dump',
            '--force'               => true,
            '--print-only-filename' => true,
            '--dry-run'             => null,
        ];

        $defaults = [
            '--add-time' => true,
        ];

        $this->assertDisplayRegExp($mandatory + $options + $defaults, $regex);
    }

    public function testWithStripOption()
    {
        $input = [
            'command'        => 'db:dump',
            '--add-time'     => true,
            '--only-command' => true,
            '--force'        => true,
            '--strip'        => '@development not_existing_table_1',
            '--compression'  => 'gzip',
        ];

        $dbConfig = $this->getDatabaseConnection()->getConfig();
        $db = $dbConfig['dbname'];

        $this->assertDisplayRegExp($input, "/--ignore-table=$db.customer_entity/");
        $this->assertDisplayRegExp($input, "/--ignore-table=$db.customer_address_entity/");
        $this->assertDisplayRegExp($input, "/--ignore-table=$db.sales_order/");
        $this->assertDisplayRegExp($input, "/--ignore-table=$db.sales_order_item/");
        $this->assertDisplayRegExp($input, "/--ignore-table=$db.sales_order_item/");
        $this->assertDisplayNotContains($input, "not_existing_table_1");
        $this->assertDisplayContains($input, ".sql.gz");

        /**
         * Uncompressed
         */
        $this->assertDisplayNotContains(
            [
                'command'        => 'db:dump',
                '--add-time'     => true,
                '--only-command' => true,
                '--force'        => true,
                '--strip'        => '@development',
            ],
            ".sql.gz"
        );
    }

    public function testWithExcludeOption()
    {
        $input = [
            'command'        => 'db:dump',
            '--add-time'     => true,
            '--only-command' => true,
            '--force'        => true,
            '--exclude'      => 'core_config_data',
            '--compression'  => 'gzip',
        ];

        $dbConfig = $this->getDatabaseConnection()->getConfig();
        $db = $dbConfig['dbname'];

        $this->assertDisplayRegExp($input, "/--ignore-table=$db.core_config_data/");
        $this->assertDisplayNotContains($input, "not_existing_table_1");
        $this->assertDisplayContains($input, ".sql.gz");

        /**
         * Uncompressed
         */
        $this->assertDisplayNotContains(
            [
                'command'        => 'db:dump',
                '--add-time'     => true,
                '--only-command' => true,
                '--force'        => true,
                '--exclude'      => 'core_config_data',
            ],
            ".sql.gz"
        );
    }

    public function testWithIncludeOptions()
    {
        $input = [
            'command'        => 'db:dump',
            '--add-time'     => true,
            '--only-command' => true,
            '--force'        => true,
            '--include'      => 'core_config_data',
            '--compression'  => 'gzip',
        ];

        $dbConfig = $this->getDatabaseConnection()->getConfig();
        $db = $dbConfig['dbname'];

        $this->assertDisplayNotContains($input, "--ignore-table=$db.core_config_data");
        $this->assertDisplayContains($input, "--ignore-table=$db.catalog_product_entity");
        $this->assertDisplayContains($input, ".sql.gz");
    }

    public function testExecuteWithMydumper()
    {
        if (OperatingSystem::isProgramInstalled('mydumper') === false) {
            $this->markTestSkipped('mydumper is not installed');
        }

        $input = [
            'command'        => 'db:dump',
            '--add-time'     => true,
            '--only-command' => true,
            '--force'        => true,
            '--compression'  => 'gz',
            '--mydumper'     => true,
        ];

        $this->assertDisplayContains($input, 'mydumper');
        $this->assertDisplayContains($input, '--outputdir=');
        $this->assertDisplayContains($input, '--database=');
    }

    public function testWithStripOptionMydumper()
    {
        if (OperatingSystem::isProgramInstalled('mydumper') === false) {
            $this->markTestSkipped('mydumper is not installed');
        }

        $input = [
            'command'        => 'db:dump',
            '--add-time'     => true,
            '--only-command' => true,
            '--force'        => true,
            '--strip'        => '@development not_existing_table_1',
            '--compression'  => 'gzip',
            '--mydumper'     => true,
        ];

        $this->assertDisplayRegExp($input, '/--no-data=(\'|\")?customer_entity(\'|\")?/');
        $this->assertDisplayRegExp($input, '/--no-data=(\'|\")?customer_address_entity(\'|\")?/');
        $this->assertDisplayRegExp($input, '/--no-data=(\'|\")?sales_order(\'|\")?/');
        $this->assertDisplayNotContains($input, "not_existing_table_1");
    }

    public function testWithExcludeOptionMydumper()
    {
        if (OperatingSystem::isProgramInstalled('mydumper') === false) {
            $this->markTestSkipped('mydumper is not installed');
        }

        $input = [
            'command'        => 'db:dump',
            '--add-time'     => true,
            '--only-command' => true,
            '--force'        => true,
            '--exclude'      => 'core_config_data',
            '--compression'  => 'gzip',
            '--mydumper'     => true,
        ];

        $this->assertDisplayContains($input, '--ignore-table');
        $this->assertDisplayContains($input, 'core_config_data');
        $this->assertDisplayNotContains($input, "not_existing_table_1");
    }

    public function testWithIncludeExcludeOptions()
    {
        $input = [
            'command'        => 'db:dump',
            '--add-time'     => true,
            '--only-command' => true,
            '--force'        => true,
            '--include'      => 'admin_user',
            '--exclude'      => 'admin_user',
        ];

        $dbConfig = $this->getDatabaseConnection()->getConfig();
        $db = $dbConfig['dbname'];

        // in the structure dump should be the table name explicitly listed
        $this->assertDisplayContains($input, "admin_user");

        // The table should be included, even though it is also excluded
        $this->assertDisplayNotContains($input, "--ignore-table=$db.admin_user ");
    }
}
